package core

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
	"tone-agent/entity"

	"github.com/spf13/viper"
)

func PullTask() []entity.Task {
	url := fmt.Sprintf("%s/%s", viper.Get(entity.ProxyConfigKey), entity.PullTaskAPI)
	tsn := viper.GetString("tsn")
	sign := GetSign()
	data := map[string]string{"tsn": tsn, "sign": sign}
	jsonData, _ := json.Marshal(data)
	client := GetHttpClient()
	resp, err := client.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		MyLogger.WithFields(LogFields{
			"error": err.Error(),
		}).Error("Pull task from toneagent-proxy failed")
		return nil
	}
	if resp.StatusCode != 200 {
		MyLogger.WithFields(LogFields{
			"error": resp.StatusCode,
		}).Error("Pull task from toneagent-proxy failed")
		return nil
	}
	result, err2 := ioutil.ReadAll(resp.Body)
	if err2 != nil {
		MyLogger.WithFields(LogFields{"error": result}).Info("Pull task from toneagent-proxy failed")
		return nil
	}
	taskRes := &entity.PullTaskRes{}
	err3 := json.Unmarshal(result, taskRes)
	if err3 != nil {
		MyLogger.WithFields(LogFields{"error": string(result)}).Info("Pull task from toneagent-proxy failed")
		return nil
	}
	return taskRes.Tasks
}

func ProcessTask(tasks []entity.Task) {
	for _, task := range tasks {
		ExecTask(task)
	}
}

func ExecTask(task entity.Task) map[string]string {
	var mutex = sync.Mutex{}
	mutex.Lock()

	uDec, _ := base64.StdEncoding.DecodeString(task.Script)
	script := string(uDec)

	env := strings.Split(task.Env, ",")
	env = append(env, fmt.Sprintf("PATH=%s", os.Getenv("PATH")))
	env = append(env, "HOME=/root")

	identFile := fmt.Sprintf("%s/%s",
		entity.ScriptPath,
		task.Tid)
	if FileExist(identFile) {
		MyLogger.WithFields(LogFields{"scriptFile": identFile}).Warning(
			"The script file already exists, but the system tries to generate it repeatedly")
		return nil
	}
	if err := WriteFile(identFile, []byte(script), 0777); err != nil {
		MyLogger.WithFields(LogFields{
			"error": err.Error(),
			"file":  identFile,
		}).Error("Save script of task to file failed!")
		return nil
	}

	mutex.Unlock()

	MyLogger.WithFields(LogFields{
		"tid":     task.Tid,
		"env":     task.Env,
		"args":    task.Args,
		"sync":    task.Sync,
		"timeout": task.Timeout,
		"ident":   identFile,
	}).Info("Task executing")
	ctxt, cancel := context.WithTimeout(
		context.Background(),
		time.Duration(task.Timeout)*time.Second)
	defer cancel()
	cmd := exec.CommandContext(ctxt, "/bin/bash", "-c", script)
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true, Pgid: 0}
	cmd.Dir = task.Cwd
	cmd.Env = env
	cmd.Args = append(cmd.Args, append([]string{""}, strings.Fields(task.Args)...)...)

	// 创建标准输出文件
	stdoutFile := fmt.Sprintf("%s/%s.stdout", entity.TaskOutputPath, task.Tid)
	taskStdout, err := os.Create(stdoutFile)
	if err != nil {
		MyLogger.WithFields(LogFields{"error": err.Error(), "tid": task.Tid}).Error("Failed to create output file")
		return nil
	}
	defer taskStdout.Close()

	// 创建异常文件
	errorFile := fmt.Sprintf("%s/%s.error", entity.TaskOutputPath, task.Tid)
	taskError, err := os.Create(errorFile)
	if err != nil {
		MyLogger.WithFields(LogFields{"error": err.Error(), "tid": task.Tid}).Error("Failed to create error file")
		return nil
	}
	defer taskError.Close()

	cmd.Stdout = taskStdout
	cmd.Stderr = taskError

    var children []int
    var childMutex sync.Mutex
    // 定期获取子进程的 goroutine
    go func() {
        ticker := time.NewTicker(30 * time.Second) // 每30秒检查一次子进程
        defer ticker.Stop()

        for {
            select {
            case <-ctxt.Done():
                return
            case <-ticker.C:
                currentChildren := getChildren(cmd.Process.Pid)
                childMutex.Lock()
                for _, pid := range currentChildren {
                    if !contains(children, pid) {
                        children = append(children, pid)
                    }
                }
                childMutex.Unlock()
            }
        }
    }()

	// 在命令启动后监控context的超时
	go func() {
		<-ctxt.Done() // 等待超时信号
		if ctxt.Err() == context.DeadlineExceeded {
		    // 使用 int(pid)杀死子进程组
            for _, pid := range children {
                syscall.Kill(-int(pid), syscall.SIGTERM)
				MyLogger.WithFields(LogFields{
					"tid": task.Tid,
					"children_pid": strconv.Itoa(pid),
				}).Warning("Timeout SIGTERM children pid")
            }
            // 超时时，使用 -cmd.Process.Pid 表示向该进程发送信号
			syscall.Kill(-cmd.Process.Pid, syscall.SIGTERM)
			MyLogger.WithFields(LogFields{
				"tid": task.Tid,
				"pid": strconv.Itoa(cmd.Process.Pid),
			}).Warning("Timeout SIGTERM pid")
		}
	}()

	// var b bytes.Buffer
	var taskPid int
	// cmd.Stdout = &b
	// cmd.Stderr = &b

	var resultData = map[string]string{}
	if viper.GetString(entity.ModeConfigKey) == entity.ActiveMode {
		if CheckRebootCommand(script) {
			resultData = map[string]string{
				"status": entity.TaskCompletedStatus,
				"tid":    task.Tid,
				// 				"task_pid": strconv.Itoa(taskPid),
				"result": "Reboot command executed successfully",
			}
			//SyncResult(resultData)
			SaveResult(resultData)
			cmd.Start()
			//os.Remove(scriptFile)
		} else {
			cmd.Start()
			taskPid = cmd.Process.Pid
			resultData = map[string]string{
				"start_time": GetCurTimeStr(),
				"status":     entity.TaskRunningStatus,
				"tid":        task.Tid,
				"task_pid":   strconv.Itoa(taskPid),
			}
		}
		SaveResult(resultData)
		SyncResult(resultData)
	} else {
		cmd.Start()
		taskPid = cmd.Process.Pid
		resultData = map[string]string{
			"start_time": GetCurTimeStr(),
			"status":     entity.TaskRunningStatus,
			"tid":        task.Tid,
			"task_pid":   strconv.Itoa(taskPid),
		}
		SaveResult(resultData)
	}
	var (
		errorCode string
		errorMsg  string
		exitCode  string
	)
	if execErr := cmd.Wait(); execErr != nil {
		errorCode = entity.ExecCmdErrorCode
		errorMsg = fmt.Sprintf("%s(%s)", entity.ExecCmdErrorMsg, execErr.Error())

		if exitErr, ok := execErr.(*exec.ExitError); ok {
			if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
				exitCode = strconv.Itoa(status.ExitStatus())
			}
		}
	}

	stdout, _ := os.ReadFile(stdoutFile)
	result := string(stdout)
	resultData = map[string]string{
		"status": entity.TaskCompletedStatus,
		"tid":    task.Tid,
		//"script":     task.Script,
		"task_pid":    strconv.Itoa(taskPid),
		"error_code":  errorCode,
		"error_msg":   errorMsg,
		"result":      result,
		"exit_code":   exitCode,
		"finish_time": GetCurTimeStr(),
	}
	MyLogger.WithFields(LogFields{
		"tid":     task.Tid,
		"env":     task.Env,
		"args":    task.Args,
		"timeout": task.Timeout,
		"ident":   identFile,
		"result":  result,
	}).Info("Task execution successfully")
	//SaveResult(resultData)
	UpdateResult(resultData)
	os.Remove(identFile)
	os.Remove(stdoutFile)
	os.Remove(errorFile)
	return resultData
}

func getChildren(parentPid int) []int {
	cmd := exec.Command("ps", "-o", "pid", "--ppid", strconv.Itoa(parentPid))
    output, err := cmd.Output()
    if err != nil {
        return []int{}
    }
    var children []int
	lines := strings.Split(strings.TrimSpace(string(output)), "\n")
    for _, line := range lines[1:] { // 第一行是表头
        pid, err := strconv.Atoi(strings.TrimSpace(line))
        if err == nil {
            children = append(children, pid)
        }
    }
	return children
}

func contains(slice []int, item int) bool {
    for _, s := range slice {
        if s == item {
            return true
        }
    }
    return false
}

//func ExecTask(task entity.Task) map[string]string {
//	// 使用伪终端的方式执行任务
//	uDec, _ := base64.StdEncoding.DecodeString(task.Script)
//	script := string(uDec)
//
//	env := strings.Split(task.Env, ",")
//	env = append(env, fmt.Sprintf("PATH=%s", os.Getenv("PATH")))
//	env = append(env, "HOME=/root")
//	env = append(env, "TERM=xterm")
//
//	scriptFile := fmt.Sprintf("%s/%s.sh",
//		entity.ScriptPath,
//		task.Tid)
//	if err := WriteFile(scriptFile, []byte(script), 0777); err != nil {
//		MyLogger.WithFields(LogFields{
//			"error": err.Error(),
//			"file":  scriptFile,
//		}).Info("Write task's result to file failed!")
//		return nil
//	}
//	execCmd := fmt.Sprintf("%s %s", scriptFile, task.Args)
//	MyLogger.WithFields(LogFields{
//		"tid":     task.Tid,
//		"env":     task.Env,
//		"args":    task.Args,
//		"sync":    task.Sync,
//		"timeout": task.Timeout,
//		"script":  scriptFile,
//	}).Info("Task executing")
//	ctxt, cancel := context.WithTimeout(
//		context.Background(),
//		time.Duration(task.Timeout)*time.Second)
//	defer cancel()
//	cmd := exec.CommandContext(ctxt, "/bin/bash", "-c", execCmd)
//	//cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true, Pgid: 0}
//	cmd.Dir = task.Cwd
//	cmd.Env = env
//
//	// 创建伪终端
//	ptmx, err := pty.Start(cmd)
//	if err != nil {
//		log.Fatalf("Failed to create pty: %v", err)
//	}
//
//	// 从伪终端读取输出
//	output := make([]byte, 0)
//	buffer := make([]byte, 1024)
//	for {
//		n, err := ptmx.Read(buffer)
//		if err != nil {
//			break
//		}
//		output = append(output, buffer[:n]...)
//	}
//	taskPid := cmd.Process.Pid
//	var resultData = map[string]string{}
//	if viper.Get(entity.ModeConfigKey) == entity.ActiveMode {
//		if CheckRebootCommand(script) {
//			resultData = map[string]string{
//				"status":   entity.TaskCompletedStatus,
//				"tid":      task.Tid,
//				"task_pid": strconv.Itoa(taskPid),
//				"result":   "Reboot command executed successfully",
//			}
//			SyncResult(resultData)
//			SaveResult(resultData)
//			os.Remove(scriptFile)
//		} else {
//			resultData = map[string]string{
//				"start_time": GetCurTimeStr(),
//				"status":     entity.TaskRunningStatus,
//				"tid":        task.Tid,
//				"task_pid":   strconv.Itoa(taskPid),
//			}
//		}
//		SaveResult(resultData)
//		go SyncResult(resultData)
//	} else {
//		resultData = map[string]string{
//			"start_time": GetCurTimeStr(),
//			"status":     entity.TaskRunningStatus,
//			"tid":        task.Tid,
//			"task_pid":   strconv.Itoa(taskPid),
//		}
//		SaveResult(resultData)
//	}
//	var (
//		errorCode string
//		errorMsg  string
//		exitCode  string
//	)
//	if execErr := cmd.Wait(); execErr != nil {
//		errorCode = entity.ExecCmdErrorCode
//		errorMsg = fmt.Sprintf("%s(%s)", entity.ExecCmdErrorMsg, execErr.Error())
//
//		if exitErr, ok := execErr.(*exec.ExitError); ok {
//			if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
//				exitCode = strconv.Itoa(status.ExitStatus())
//			}
//		}
//	}
//	result := string(output)
//	resultData = map[string]string{
//		"status": entity.TaskCompletedStatus,
//		"tid":    task.Tid,
//		//"script":     task.Script,
//		"task_pid":    strconv.Itoa(taskPid),
//		"error_code":  errorCode,
//		"error_msg":   errorMsg,
//		"result":      result,
//		"exit_code":   exitCode,
//		"finish_time": GetCurTimeStr(),
//	}
//	MyLogger.WithFields(LogFields{
//		"tid":     task.Tid,
//		"env":     task.Env,
//		"args":    task.Args,
//		"timeout": task.Timeout,
//		"script":  scriptFile,
//		"result":  result,
//	}).Info("Task executed successfully")
//	//SaveResult(resultData)
//	UpdateResult(resultData)
//	os.Remove(scriptFile)
//	return resultData
//}
